﻿
#include <intrins.h>
#include "w5500.h"

/*
空读一次SPI。SPI读写前初始化"脉冲"，因为有时候相隔一段时间后
第一个读取或者写入的字节会失败，所以先读写0一次。
*/
#define W5500_NOP() 		do {  	W5500_MISO = 0; \
									W5500_MOSI = 0; \
									w5500_read_byte(0x00); \
								} while(0)

//网卡物理地址
static unsigned char ethmac[6] = { PHYADDR0,
	PHYADDR1, PHYADDR2, PHYADDR3, PHYADDR4, PHYADDR5 };
//网卡IP地址
static unsigned char ipaddr[4] = { IPADDR0, IPADDR1,
	IPADDR2, IPADDR3 };
//网卡子网掩码
static unsigned char netmask[4] = { NETMASK0, NETMASK1,
	NETMASK2, NETMASK3 };
//网卡网关地址
static unsigned char dripaddr[4] = { DRIPADDR0, DRIPADDR1, DRIPADDR2,
	DRIPADDR3 };
unsigned char w5500_flags; //中断标志

//延迟 ms
static void delay(unsigned int x)
{
    unsigned int i,j;

    for(j=0;j<5;j++)
		for(i=0;i<x;i++);
}

//SPI读写时序，这里是模式0，具体可以参考SPI模式0时序图
static unsigned char spi_process(unsigned char dat)
{
	unsigned char i, ret = 0;

	for(i = 0; i < 8; i++) {
		//判断最高位是否为0
		if(dat & 0x80)
			W5500_MOSI = 1; //给高电平
		else
			W5500_MOSI = 0; //给低电平
		dat <<= 1;
		W5500_SCLK = 1; 	//SCK上升沿写入
		ret <<= 1;
		ret |= W5500_MISO;  //上升沿写入的同时读取
		W5500_SCLK = 0;   	//恢复默认电平
	}
	return ret;
}

//SPI写入一个字节
static void spi_write(unsigned char dat)
{
	spi_process(dat);
}

//SPI读取一个字节
static unsigned char spi_read(void)
{
	return spi_process(0x00);
}

//SPI写入2个字节
static void spi_write_short(unsigned int dat)
{
	spi_write((unsigned char)(dat >> 8));  //先写入高8位字节
	spi_write((unsigned char)(dat & 0x00ff));
}

//向W5500通用寄存器写入1字节数据，参数1：寄存器地址，参数二：数据
static void w5500_write_byte(unsigned int reg, unsigned char dat)
{
	W5500_SCS = 0;	//置W5500的SCS为低电平
	spi_write_short(reg); //通过SPI写16位寄存器地址
	spi_write(FDM1|RWB_WRITE|COMMON_R); //通过SPI写控制字节,1个字节数据长度,写数据,选择通用寄存器
	spi_write(dat); //写1个字节数据
	W5500_SCS = 1; //置W5500的SCS为高电平
}

//向W5500通用寄存器写入2个字节数据，参数1：寄存器地址，参数二：数据
static void w5500_write_short(unsigned int reg, unsigned int dat)
{
	W5500_SCS = 0;
	spi_write_short(reg); //通过SPI写16位寄存器地址
	spi_write(FDM2|RWB_WRITE|COMMON_R); //通过SPI写控制字节,2个字节数据长度,写数据,选择通用寄存器
	spi_write_short(dat); //写16位数据
	W5500_SCS = 1;
}

//向W5500通用寄存器写入X个字节数据，参数1：寄存器地址，参数二：数据指针，参数三：数据大小
static void w5500_write_xbit(unsigned int reg, unsigned char *pdat,
	unsigned int size)
{
	unsigned int i;
	W5500_SCS = 0;
	spi_write_short(reg);
	spi_write(VDM|RWB_WRITE|COMMON_R); 	//写入可X字节，写入字节数由CS控制，FDM1写入1字节，FDM2二字节，VDM可变字节
	for(i = 0; i < size; i++) {
		spi_write(*pdat);
		++pdat;
	}
	W5500_SCS = 1;
}

//向W5500的SOCKET寄存器写入一个字节数据，参数1：SOCKET，参数二：寄存器地址，参数三：数据
static void w5500_wirte_sock_byte(SOCKET s, unsigned int reg,
	unsigned char dat)
{
	W5500_SCS = 0;
	spi_write_short(reg); //通过SPI写16位寄存器地址
	spi_write(FDM1|RWB_WRITE|(s*0x20+0x08));  //通过SPI写控制字节,1个字节数据长度,写数据,选择端口s的寄存器
	spi_write(dat); //写1个字节数据
	W5500_SCS = 1;
}

//向W5500的SOCKET寄存器写入2个字节数据，参数1：SOCKET，参数二：寄存器地址，参数三：数据
static void w5500_wirte_sock_short(SOCKET s, unsigned int reg,
	unsigned int dat)
{
	W5500_SCS = 0;
	spi_write_short(reg);
	spi_write(FDM2|RWB_WRITE|(s*0x20+0x08));
	spi_write_short(dat);
	W5500_SCS = 1;
}

//向W5500的通用寄存器读取一个字节数据，参数1：寄存器地址
static unsigned char w5500_read_byte(unsigned int reg)
{
	unsigned char ret;
	W5500_SCS = 0;
	spi_write_short(reg);
	spi_write(FDM1|RWB_READ|COMMON_R);
	ret = spi_read();
	W5500_SCS = 1;
	return ret;
}

//向W5500的SOCKET寄存器读取一个字节数据，参数1：SOCKET，参数二：寄存器地址
static unsigned char w5500_read_sock_byte(SOCKET s, unsigned int reg)
{
	unsigned char ret;
	W5500_SCS = 0;
	spi_write_short(reg);
	spi_write(FDM1|RWB_READ|(s*0x20+0x08));
	ret = spi_read();
	W5500_SCS = 1;
	return ret;
}

//向W5500的SOCKET寄存器读取两个字节的数据，参数1：SOCKET，参数二：寄存器地址
static unsigned int w5500_read_sock_short(SOCKET s,
	 unsigned int reg)
{
	unsigned int ret;
	W5500_SCS = 0;
	spi_write_short(reg);
	spi_write(FDM2|RWB_READ|(s*0x20+0x08));
	ret = spi_read();
	ret <<= 8;
	ret |= spi_read();
	W5500_SCS = 1;
	return ret;
}

//向W5500的SOCKET数据缓冲器读取接收到的数据包，参数1：SOCKET，参数二，数据缓冲区，参数三：
//缓冲区最大大小
static unsigned int w5500_read_sock_buffer(SOCKET s,
 	unsigned char *pbuf, unsigned int mxsize)
{
	unsigned int rx_size;
	unsigned int offset, offset1;
	unsigned int i;
	unsigned char j;
	unsigned int count;

	W5500_NOP();
	rx_size = w5500_read_sock_short(s, Sn_RX_RSR);
	if(rx_size == 0)
		return 0; //没接收到数据则返回
	if(rx_size > 1460)
		rx_size = 1460;
	W5500_NOP();
	offset = w5500_read_sock_short(s, Sn_RX_RD);
	offset1 = offset;
	offset &= (S_RX_SIZE-1); //计算实际的物理地址
	W5500_SCS = 0; //置W5500的SCS为低电平
	count = 0;
	spi_write_short(offset); //写16位地址
	spi_write(VDM|RWB_READ|(s*0x20+0x18)); //写控制字节,N个字节数据长度,读数据,选择端口s的寄存器
	//如果最大地址未超过W5500接收缓冲区寄存器的最大地址
	if((offset+rx_size) < S_RX_SIZE) {
		//循环读取rx_size个字节数据
		for(i = 0; i < rx_size; i++) {
			j = spi_read();
			//超过mxsize的数据将被遗弃
			if(++count <= mxsize) {
				*pbuf = j; //将读取到的数据保存到数据保存缓冲区
				++pbuf; //数据保存缓冲区指针地址自增1
			}
		}
	} else {
		offset = S_RX_SIZE - offset;
		//循环读取出前offset个字节数据
		for(i = 0; i < offset; i++) {
			j = spi_read();
			if(++count <= mxsize) {
				*pbuf = j;
				++pbuf;
			}
		}
		W5500_SCS = 1;
		W5500_SCS = 0;
		spi_write_short(0x00); //写16位地址
		spi_write(VDM|RWB_READ|(s*0x20+0x18));
		//循环读取后rx_size-offset个字节数据
		for(; i < rx_size; i++) {
			j = spi_read();
			if(++count <= mxsize) {
				*pbuf = j;
				++pbuf;
			}
		}
	}
	offset1 += rx_size; //更新实际物理地址,即下次读取接收到的数据的起始地址
	w5500_wirte_sock_short(s, Sn_RX_RD, offset1);
	w5500_wirte_sock_byte(s, Sn_CR, RECV); //发送启动接收命令
	W5500_SCS = 1;
	return count < mxsize ? rx_size : mxsize;
}

//向W5500的SOCKET数据缓冲器写入数据，参数1：SOCKET，参数二，数据缓冲区，参数三：
//数据大小
static void w5500_write_sock_buffer(SOCKET s,
	unsigned char *pbuf, unsigned int size)
{
	unsigned int offset,offset1;
	unsigned int i;

	W5500_NOP();
	offset = w5500_read_sock_short(s, Sn_TX_WR);
	offset1 = offset;
	offset &= (S_TX_SIZE-1); //计算实际的物理地址

	W5500_SCS = 0;
	spi_write_short(offset);
	spi_write(VDM|RWB_WRITE|(s*0x20+0x10));
	//如果最大地址未超过W5500发送缓冲区寄存器的最大地址
	if((offset+size) < S_TX_SIZE) {
		for(i = 0; i < size; i++) {
			spi_write(*pbuf);
			++pbuf;
		}
	} else {
		offset = S_TX_SIZE - offset;
		for(i = 0; i < offset; i++) {
			spi_write(*pbuf);
			++pbuf;
		}
		W5500_SCS = 1;
		W5500_SCS = 0;
		spi_write_short(0x00);
		spi_write(VDM|RWB_WRITE|(s*0x20+0x10));
		for(; i < size; i++) {
			spi_write(*pbuf);
			++pbuf;
		}

	}
	W5500_SCS = 1;
	offset1 += size;
	//更新实际物理地址,即下次写待发送数据到发送数据缓冲区的起始地址
	w5500_wirte_sock_short(s, Sn_TX_WR, offset1);
	w5500_wirte_sock_byte(s, Sn_CR, SEND); //发送启动发送命令
}

//W5500硬重启
void w5500_hardware_reset(void)
{
	unsigned char k;
	W5500_RST = 0; 	//拉低RST引脚电平，硬复位
	delay(100); 	//低电平持续时间
	W5500_RST = 1; 	//拉高RST引脚，恢复工作状态
	delay(100);
	//引脚初始化
	W5500_SCLK = 0;
	W5500_SCS = 1;
	W5500_MOSI = 0;
	W5500_MISO = 0;
	//检测PHYCFGR寄存器的第一位值，检测是否连接上网络设备
	while(1) {
		k = w5500_read_byte(PHYCFGR);
		if((k & LINK) != 0)
			break; 		//连接上，就退出
	}
}

//W5500初始化
void w5500_init(void)
{
	unsigned char i = 0;

	w5500_flags = 0;
	w5500_write_byte(MR, RST); 		//发送软复位命令
	delay(100); 					//给内部寄存器初始化时间
	w5500_write_xbit(GAR, dripaddr, 4); //设置网关地址
	w5500_write_xbit(SUBR, netmask, 4); //设置子网掩码
	w5500_write_xbit(SHAR, ethmac, 6); //设置MAC地址
	w5500_write_xbit(SIPR, ipaddr, 4); //设置设置IP地址
	//32KB缓存，每个SOCKET给4K，分别给每个SOCKET的接收和发送缓冲区，具体参考
	//w5500手册
	for(i = 0; i < 8; i++) {
		w5500_wirte_sock_byte(i, Sn_RXBUF_SIZE, 0x02);
		w5500_wirte_sock_byte(i, Sn_TXBUF_SIZE, 0x02);
	}
	//设置重试时间，默认为2000(200ms)
	//每一单位数值为100微秒,初始化时值设为2000(0x07D0),等于200毫秒
	w5500_write_short(RTR, 0x0fa0);
	//设置重试次数，默认为8次
	//如果重发的次数超过设定值,则产生超时中断(相关的端口中断寄存器中的Sn_IR 超时位(TIMEOUT)置“1”)
	w5500_write_byte(RCR, 10);
	w5500_write_byte(SIMR, S0_IMR); 		//开启W5500的SOCKET0中断
}

void socket_init(SOCKET s, unsigned int port)
{
	switch(s) {
		case SOCKET0:
			W5500_NOP();
			w5500_wirte_sock_short(SOCKET0, Sn_MSSR, 1400);  //设置MTU分片
			w5500_wirte_sock_short(SOCKET0, Sn_PORT,
				port); //打开socket0的端口
			w5500_wirte_sock_byte(SOCKET0, Sn_TTL, 64); 	//IP数据包生存时间
			break;
		case SOCKET1:
			break;
		case SOCKET2:
			break;
		case SOCKET3:
			break;
		case SOCKET4:
			break;
		case SOCKET5:
			break;
		case SOCKET6:
			break;
		case SOCKET7:
			break;
	}
}

unsigned char socket_listen(SOCKET s)
{
	unsigned char ret;

	W5500_NOP();
	w5500_wirte_sock_byte(s, Sn_MR, MR_TCP); //设置socket为TCP模式
	w5500_wirte_sock_byte(s, Sn_CR, OPEN); //打开Socket
	ret = w5500_read_sock_byte(s, Sn_SR);
	if(ret != SOCK_INIT) {
		w5500_wirte_sock_byte(s, Sn_CR, CLOSE); //打开不成功,关闭Socket
		return 0;
	}
	w5500_wirte_sock_byte(s, Sn_CR, LISTEN); //设置Socket为侦听模式
	ret = w5500_read_sock_byte(s, Sn_SR);
	if(ret != SOCK_LISTEN) {
		w5500_wirte_sock_byte(s, Sn_CR, CLOSE);
		return 0;
	}
	return 1;
	//至此完成了Socket的打开和设置侦听工作,至于远程客户端是否与它建立连接,则需要等待Socket中断，
	//以判断Socket的连接是否成功。参考W5500数据手册的Socket中断状态
	//在服务器侦听模式不需要设置目的IP和目的端口号
}

//关闭SOCKET
void socket_close(SOCKET s)
{
	W5500_NOP();
	w5500_wirte_sock_byte(s, Sn_CR, CLOSE); //发送关闭命令
}

//接收数据
unsigned int socket_recv(SOCKET s, unsigned char *pbuf, unsigned int mxsize)
{
	return w5500_read_sock_buffer(s, pbuf, mxsize);
}

//发送数据
void socket_send(SOCKET s, unsigned char *pbuf, unsigned int buf_size)
{
	w5500_write_sock_buffer(s, pbuf, buf_size);
}

//中断处理
void w5500_irq_process(void)
{
	unsigned char i,j;

W5500INT:
	W5500_NOP();
	i = w5500_read_byte(SIR); //读取中断标志
	//SOCKET0中断
	if(i & S0_INT) {
		j = w5500_read_sock_byte(SOCKET0, Sn_IR);
		w5500_wirte_sock_byte(SOCKET0, Sn_IR, j); 	//回写中断标志，复位INT引脚
		w5500_flags &= W5500_IRQ_MARK;  		//清除标志位
		//连接成功
		if(j & IR_CON) {
			w5500_flags |= W5500_SOCKET_CONN;
		}
		//断开连接
		if(j & IR_DISCON) {
			w5500_flags |= W5500_SOCKET_DISCONN;
		}
		//发送成功
		if(j & IR_SEND_OK) {
			w5500_flags |= W5500_SOCKET_SNDOK;
		}
		//接收成功
		if(j & IR_RECV) {
			w5500_flags |= W5500_SOCKET_RECVOK;
		}
		//超时
		if(j & IR_TIMEOUT) {
			w5500_flags |= W5500_SOCKET_TIMEOUT;
		}
		W5500_IRQ_CALLBACK(); //回调函数
	}
	//SOCKET1中断
	if(i & S1_INT) {

	}
	//SOCKET2中断
	if(i & S2_INT) {

	}
	//SOCKET3中断
	if(i & S3_INT) {

	}
	//SOCKET4中断
	if(i & S4_INT) {

	}
	//SOCKET5中断
	if(i & S5_INT) {

	}
	//SOCKET6中断
	if(i & S6_INT) {

	}
	//SOCKET7中断
	if(i & S7_INT) {

	}

	W5500_NOP();
	i = w5500_read_byte(SIR);  //检测是否又有中断触发
	if(i != 0)
		goto W5500INT;
	W5500_RESET_IRQMARK();
}